﻿// $Id: $
// $DateTime: $
// Copyright © 2008 Tristen Fielding

using System;
using System.CodeDom;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.Globalization;
using System.IO;
using System.Resources;
using System.Runtime.InteropServices;
using System.Security;
using System.Text.RegularExpressions;
using System.Threading;

using Microsoft.VisualStudio.OLE.Interop;
using Microsoft.VisualStudio.Shell.Interop;

using ResXFileCodeGenerator.Internal;
using ResXFileCodeGenerator.Microsoft;

namespace ResXFileCodeGenerator
{
    /// <summary>
    /// The base class that is responsible for creating the various the resX wrapper classes.
    /// </summary>
    public abstract class ResXFileCodeGeneratorBase : CodeGeneratorBaseWithSite
    {
        protected ResXFileCodeGeneratorBase(string customToolName, Visibility classVisibility, Partial partialClass)
        {
            if(string.IsNullOrEmpty(customToolName))
            {
                throw new ArgumentNullException("customToolName");
            }

            CustomToolName = customToolName;
            ClassVisibility = classVisibility;
            PartialClass = partialClass;

            InvalidResources = new List<ResourceError>();
        }

        /// <summary>
        /// The method that does the actual work of generating code given the input file
        /// </summary>
        /// <param name="inputFileContent">File contents as a string</param>
        /// <returns>The generated code file as a UTF-8 stream.</returns>
        protected sealed override Stream GenerateCode(string inputFileContent)
        {
            if(inputFileContent == null)
            {
                throw new ArgumentNullException("inputFileContent");
            }

            ClassName = CalculateClassName();
            ClassNamespace = CalculateClassNamespace();
            ResourceNamespace = CalculateResourceNamespace();

            InvalidResources.Clear();

            IDictionary<string, ResourceData> resourceNodes = GetValidResourcesFrom(inputFileContent);

            StreamWriter codeStream = BuildDesignerFile(resourceNodes);

            ReportAnyInvalidResources(codeStream);
            codeStream.Flush();

            return codeStream.BaseStream;
        }

        protected sealed override string GetDefaultExtension()
        {
            return Strings.DesignerExt + base.GetDefaultExtension();
        }

        private IDictionary<string, ResourceData> GetValidResourcesFrom(string inputFileContent)
        {
            IDictionary<string, ResourceData> resourceNodes = GetRawResourcesFrom(inputFileContent);

            return ConvertResNamesToValidIdentifierNames(resourceNodes);
        }

        private IDictionary<string, ResourceData> GetRawResourcesFrom(string fileContent)
        {
            IDictionary<string, ResourceData> nodes = new Dictionary<string, ResourceData>(StringComparer.InvariantCultureIgnoreCase);

            using (ResXResourceReader resourceReader = ResXResourceReader.FromFileContents(fileContent))
            {
                resourceReader.UseResXDataNodes = true;
                string inputDirectoryName = Path.GetDirectoryName(InputFilePath);
                resourceReader.BasePath = Path.GetFullPath(inputDirectoryName);

                foreach(DictionaryEntry resourceEntry in resourceReader)
                {
                    ResourceData node = new ResourceData((ResXDataNode) resourceEntry.Value);

                    nodes.Add((string) resourceEntry.Key, node);
                }
            }
            return nodes;
        }

        private IDictionary<string, ResourceData> ConvertResNamesToValidIdentifierNames(IDictionary<string, ResourceData> resourceNodes)
        {
            IDictionary<string, ResourceData> validResources = new Dictionary<string, ResourceData>();

            foreach(ResourceData item in resourceNodes.Values)
            {
                string resourceName = item.ResName;

                // Resource names can't be named the same as default member names and
                // we can't create a method or a property that returns void so check 
                // for that case as well.
                //
                if (resourceName == Strings.ResourceManagerPropertyName ||
                    resourceName == Strings.CultureInfoPropertyName ||
                    resourceName == Strings.InnerClassName ||
                    item.ResType == typeof(void))
                {
                    InvalidResources.Add(new ResourceError(item,
                        string.Format(CultureInfo.CurrentCulture, Strings.CannotCreateIdentifierForResource, resourceName)));

                    continue;
                }

                // Skip special resource names, those that start with '$' or ">>"
                //
                if (resourceName.StartsWith("$") || resourceName.StartsWith(">>"))
                    continue;

                string identifierName = ConvertResNameToValidIdentifierName(resourceName, false);
                if (String.IsNullOrEmpty(identifierName))
                {
                    InvalidResources.Add(new ResourceError(item,
                        string.Format(CultureInfo.CurrentCulture, Strings.CannotCreateIdentifierForResource, resourceName)));

                    continue;
                }

                item.IdentifierName = identifierName;
                validResources.Add(resourceName, item);
            }

            return validResources;
        }

        private string ConvertResNameToValidIdentifierName(string resourceName, bool isNamespace)
        {
            if (CodeProvider.IsValidIdentifier(resourceName))
                return resourceName;

            string badCharPattern = @"[ ~!@#%^&*()-+[\]\{}|;'"",/<>?" + "\x00a0" + (isNamespace ? "" : @":\.") + "]";

            string newIdentifierName = Regex.Replace(resourceName, badCharPattern, "_");
            if (CodeProvider.IsValidIdentifier(newIdentifierName))
                return newIdentifierName;

            try
            {
                newIdentifierName = CodeProvider.CreateValidIdentifier(newIdentifierName);
                if(CodeProvider.IsValidIdentifier(newIdentifierName))
                    return newIdentifierName;
            }
            catch(NotImplementedException)
            {
                // Continue.
            }

            newIdentifierName = "_" + newIdentifierName;
            if (CodeProvider.IsValidIdentifier(newIdentifierName))
                return newIdentifierName;

            return null;
        }

        private string CalculateClassName()
        {
            string className = Path.GetFileNameWithoutExtension(InputFilePath);
            string validClassName = ConvertResNameToValidIdentifierName(className, false);

            if (string.IsNullOrEmpty(validClassName))
            {
                throw new ArgumentException(
                    string.Format(CultureInfo.CurrentCulture, Strings.InvalidIdentifier, className));
            }

            return validClassName;
        }

        private string CalculateClassNamespace()
        {
            string classNamespace = DefaultNamespace;
            string validClassNamespace = ConvertResNameToValidIdentifierName(classNamespace, true);

            if (string.IsNullOrEmpty(validClassNamespace))
                validClassNamespace = classNamespace;

            return validClassNamespace;
        }

        private string CalculateResourceNamespace()
        {
            string resourcesNamespace = string.Empty;

            try
            {
                IntPtr siteInterfacePointer;
                Guid vsBrowseObjectGuid = typeof(IVsBrowseObject).GUID;
                ((IObjectWithSite) this).GetSite(ref vsBrowseObjectGuid, out siteInterfacePointer);
                if (siteInterfacePointer != IntPtr.Zero)
                {
                    IVsHierarchy vsHierarchy;
                    uint pItemId;
                    object propertyValue;
                    IVsBrowseObject vsBrowseObject =
                        Marshal.GetObjectForIUnknown(siteInterfacePointer) as IVsBrowseObject;
                    Marshal.Release(siteInterfacePointer);
                    if (vsBrowseObject == null)
                        return resourcesNamespace;

                    vsBrowseObject.GetProjectItem(out vsHierarchy, out pItemId);
                    if (vsHierarchy == null)
                        return resourcesNamespace;

                    vsHierarchy.GetProperty(pItemId, (int) __VSHPROPID.VSHPROPID_DefaultNamespace,
                        out propertyValue);
                    string propertyText = propertyValue as string;
                    if (propertyText == null)
                        return resourcesNamespace;

                    resourcesNamespace = propertyText;
                }
            }
            catch (Exception ex)
            {
                if(!(ex is NullReferenceException || 
                    ex is StackOverflowException || 
                    ex is OutOfMemoryException || 
                    ex is ThreadAbortException))
                {
                    throw;
                }
            }

            return resourcesNamespace;
        }

        private StreamWriter BuildDesignerFile(IDictionary<string, ResourceData> resourceNodes)
        {
            if(resourceNodes == null)
            {
                throw new ArgumentNullException("resourceNodes");
            }

            CodeGen code = new CodeGen(CodeProvider, ClassNamespace);
            code.AddReferencedAssembly("System.dll");
            code.AddUsingNamespace("System");

            CodeTypeDeclaration outerClassDeclaration = CreateOuterClass(code, resourceNodes);
            CreateInnerClass(code, outerClassDeclaration, resourceNodes);

            return code.ToStream();
        }

        private CodeTypeDeclaration CreateOuterClass(CodeGen code, IDictionary<string, ResourceData> resourceNodes)
        {
            // Create the outer class, its comments and attributes.
            //
            CodeTypeDeclaration outerClassDeclaration = code.AddClass(ClassVisibility, PartialClass, Sealed.No, ClassName);
            code.AddSummaryComments(outerClassDeclaration, Strings.OutterClassDocComment);
            code.AddRemarkComments(outerClassDeclaration, 
                String.Format(CultureInfo.CurrentCulture, Strings.OutClassDocRemarks1, CustomToolName),
                Strings.OutClassDocRemarks2);

            code.AddGeneratedCodeAttribute(outerClassDeclaration, GetType());
            code.AddGlobalAttribute(outerClassDeclaration, typeof(DebuggerNonUserCodeAttribute));

            // The CodeDom does not support static classes or static constuctors.
            // Therefore, we will create a protected constructor instead.
            //
            CreateConstructor(code, outerClassDeclaration, Visibility.Protected);

            // Add the standard properties.
            //
            CreateResourceManagerProperty(code, outerClassDeclaration);
            CreateCultureProperty(code, outerClassDeclaration);
            CreateFormatMethod(code, outerClassDeclaration);

            foreach (var resourceError in InvalidResources)
            {
                code.AddErrorDirective(outerClassDeclaration, resourceError.Message);
            }

            // Add the resource wrapper methods.
            //
            foreach (ResourceData resource in resourceNodes.Values)
            {
                bool successful;
                if (resource.IsResString)
                {
                    successful = CreateStringResourceWrapperMember(resource, code, outerClassDeclaration);
                }
                else
                {
                    successful = CreateResourceWrapperProperty(resource, code, outerClassDeclaration);
                }

                if (!successful)
                {
                    string message = string.Format(CultureInfo.CurrentCulture,
                        Strings.CannotCreatePropertyForResource, resource.ResName);

                    code.AddErrorDirective(outerClassDeclaration, message);
                    InvalidResources.Add(new ResourceError(resource, message));
                }
            }


            // Add the standard fields.
            //
            code.AddField(outerClassDeclaration, typeof(ResourceManager), Strings.ResourceManagerVariableName,
                CodeTypeReferenceOptions.GlobalReference, Static.Yes);

            code.AddField(outerClassDeclaration, typeof(CultureInfo), Strings.CultureInfoVariableName,
                CodeTypeReferenceOptions.GlobalReference, Static.Yes);

            return outerClassDeclaration;
        }

        private static void CreateConstructor(CodeGen code, CodeTypeDeclaration classDeclaration, Visibility visibility)
        {
            code.AddConstructor(classDeclaration, visibility);
        }

        private void CreateResourceManagerProperty(CodeGen code, CodeTypeDeclaration classDeclaration)
        {
            CodeMemberProperty property = code.AddProperty(classDeclaration, typeof(ResourceManager), Strings.ResourceManagerPropertyName, true, false, 
                ClassVisibility, Static.Yes, CodeTypeReferenceOptions.GlobalReference);

            code.AddSummaryComments(property, Strings.ResourceManagerPropertyDocComments);
            
            code.AddGlobalAttribute(property, typeof(EditorBrowsableAttribute),
                new CodeAttributeArgument(code.CreateFieldRef(typeof(EditorBrowsableState), "Advanced")));

            CreateResourceManagerGetter(code, property);
        }

        private void CreateResourceManagerGetter(CodeGen code, CodeMemberProperty property)
        {
            string fullClassName = ClassName;
            if (ResourceNamespace != null)
            {
                if (ResourceNamespace.Length > 0)
                    fullClassName = ResourceNamespace + '.' + fullClassName;
            }
            else if (!string.IsNullOrEmpty(ClassNamespace))
                fullClassName = ClassNamespace + '.' + fullClassName;

            // 1. if (object.ReferenceEquals(_resourceManager, null)) 
            //    {
            CodeConditionStatement if1 = new CodeConditionStatement();
            property.GetStatements.Add(if1);

            CodeMethodInvokeExpression invoke1_ReferenceEquals = code.CreateStaticMethodCall(typeof(object), "ReferenceEquals");
            if1.Condition = invoke1_ReferenceEquals;

            CodePrimitiveExpression arg0_null = new CodePrimitiveExpression(null);
            invoke1_ReferenceEquals.Parameters.Add(Field_ResourceManager);
            invoke1_ReferenceEquals.Parameters.Add(arg0_null);


            // 2.   global::System.Resources.ResourceManager temp = 
            //         new global::System.Resources.ResourceManager(
            //            "{resourceWrapperClassFullName}", typeof({ClassName}).Assembly);
            CodeVariableDeclarationStatement var2_temp = new CodeVariableDeclarationStatement(typeof(ResourceManager), "temp");
            if1.TrueStatements.Add(var2_temp);

            CodeObjectCreateExpression new2_ResourceManager = new CodeObjectCreateExpression(new CodeTypeReference(typeof(ResourceManager)));
            var2_temp.InitExpression = new2_ResourceManager;

            CodePrimitiveExpression arg2_fullClassName = new CodePrimitiveExpression(fullClassName);
            new2_ResourceManager.Parameters.Add(arg2_fullClassName);

            CodeTypeOfExpression typeof2_Strings = new CodeTypeOfExpression(new CodeTypeReference(ClassName));
            CodePropertyReferenceExpression arg2_Assembly = new CodePropertyReferenceExpression(typeof2_Strings, "Assembly");
            new2_ResourceManager.Parameters.Add(arg2_Assembly);

            // 3.   _resourceManager = temp;
            CodeAssignStatement assignment3 = new CodeAssignStatement();
            if1.TrueStatements.Add(assignment3);

            CodeVariableReferenceExpression varRef3_ResourceManager = new CodeVariableReferenceExpression(Strings.ResourceManagerVariableName);
            CodeVariableReferenceExpression varRef3_temp = new CodeVariableReferenceExpression("temp");
            assignment3.Left = varRef3_ResourceManager;
            assignment3.Right = varRef3_temp;

            //        }
            // 4. return _resourceManager;
            //    }
            CodeMethodReturnStatement return4 = new CodeMethodReturnStatement(Field_ResourceManager);
            property.GetStatements.Add(return4);
        }

        private void CreateCultureProperty(CodeGen code, CodeTypeDeclaration classDeclaration)
        {
            CodeMemberProperty property = code.AddProperty(classDeclaration, typeof(CultureInfo), Strings.CultureInfoPropertyName, true, false,
                ClassVisibility, Static.Yes, CodeTypeReferenceOptions.GlobalReference);

            code.AddSummaryComments(property, Strings.CultureInfoPropertyDocComments1);
            
            code.AddGlobalAttribute(property, typeof(EditorBrowsableAttribute),
                new CodeAttributeArgument(code.CreateFieldRef(typeof(EditorBrowsableState), "Advanced")));

            property.GetStatements.Add(new CodeMethodReturnStatement(Field_CultureInfo));
            property.SetStatements.Add(new CodeAssignStatement(Field_CultureInfo, new CodePropertySetValueReferenceExpression()));
        }

        private void CreateFormatMethod(CodeGen code, CodeTypeDeclaration classDeclaration)
        {
            const string formatParamName = "format";
            const string argsParamName = "args";

            // 1. public static string Format(string format, params object[] args) 
            //    {
            CodeMemberMethod method =
                code.AddMethod(classDeclaration, ClassVisibility, Static.Yes, typeof(string), "Format"
                    , new []
                        {
                            new ParamInfo {ParamType = typeof(string), ParamName = formatParamName},
                            new ParamInfo {ParamType = typeof(object[]), ParamName = argsParamName, AddParamsKeyword = true},
                        }
            );

            code.AddSummaryComments(method, Strings.FormatStringDocComments1);

            // 2. return string.Format(Culture, format, args);
            //    }
            CodeMethodInvokeExpression invoke2_StringFormat = code.CreateStaticMethodCall(typeof(string), "Format");
            CodeVariableReferenceExpression arg2_CultureInfo = new CodeVariableReferenceExpression(Strings.CultureInfoPropertyName);
            CodeVariableReferenceExpression arg2_format = new CodeVariableReferenceExpression(formatParamName);
            CodeVariableReferenceExpression arg2_args = new CodeVariableReferenceExpression(argsParamName);

            invoke2_StringFormat.Parameters.Add(arg2_CultureInfo);
            invoke2_StringFormat.Parameters.Add(arg2_format);
            invoke2_StringFormat.Parameters.Add(arg2_args);

            method.Statements.Add(new CodeMethodReturnStatement(invoke2_StringFormat));
        }

        private void CreateInnerClass(CodeGen code, CodeTypeDeclaration outerClassDeclaration, IDictionary<string, ResourceData> resourceNodes)
        {
            CodeTypeDeclaration innerClassDeclaration = 
                code.AddInnerClass(outerClassDeclaration, ClassVisibility, Partial.Yes, Sealed.No, Strings.InnerClassName);

            code.AddGeneratedCodeAttribute(innerClassDeclaration, GetType());
            code.AddGlobalAttribute(innerClassDeclaration, typeof(DebuggerNonUserCodeAttribute));

            // The CodeDom does not support static classes or static constuctors.
            // Therefore, we will create a protected constructor instead.
            //
            CreateConstructor(code, innerClassDeclaration, Visibility.Private);

            foreach(ResourceData resource in resourceNodes.Values)
            {
                if (resource.IsResString)
                {
                    if (!CreateResourceWrapperProperty(resource, code, innerClassDeclaration))
                    {
                        string message = string.Format(CultureInfo.CurrentCulture,
                            Strings.CannotCreatePropertyForResource, resource.ResName);

                        code.AddErrorDirective(outerClassDeclaration, message);
                        InvalidResources.Add(new ResourceError(resource, message));
                    }
                }
            }
        }

        private bool CreateStringResourceWrapperMember(ResourceData resource, CodeGen code, CodeTypeDeclaration classDeclaration)
        {
            try
            {
                int args = 0;
                ParamInfo[] paraminfos = new ParamInfo[0];

                if (resource.IsResFile == false)
                {
                    args = StringFormatValidator.Validate(resource.ResName, resource.ResString, resource.ResComment);
                    if (args > 0)
                        paraminfos = StringFormatValidator.ParseParamInfo(resource.ResName, resource.ResComment);
                }

                if (paraminfos.Length != args)
                {
                    string message;

                    if (paraminfos.Length == 0)
                        message = string.Format(CultureInfo.CurrentUICulture, Strings.FormatComments_Missing, resource.ResName);
                    else
                        message =
                            string.Format(CultureInfo.CurrentUICulture, Strings.FormatComments_IndexOutOfRange,
                                resource.ResName);

                    code.AddErrorDirective(classDeclaration, message);
                    ReportError(message);
                    return false;
                }

                if (args > 0)
                    CreateFormattedStringWrapperMethod(resource, code, classDeclaration, paraminfos);
                else
                    CreateStringWrapperProperty(resource, code, classDeclaration);

                return true;
            }
            catch (Exception e)
            {
                ReportError(e.Message);
                return false;
            }
        }

        private void CreateFormattedStringWrapperMethod(ResourceData resource, CodeGen code, CodeTypeDeclaration classDeclaration, ParamInfo[] paraminfos)
        {
            // 1. public static string {methodName}(object arg0)
            //    {
            CodeMemberMethod method =
                code.AddMethod(classDeclaration, ClassVisibility, Static.Yes, typeof(string),
                    resource.IdentifierName, paraminfos);

            CreateStringSummaryComments(code, resource, method, true);

            // 2. return Format(Raw.{methodName}, arg0);
            //    }
            CodeMethodInvokeExpression invoke2_StringFormat = code.CreateStaticMethodCall(null, "Format");
            CodePropertyReferenceExpression propRef_innerClass = code.CreateStaticPropertyCall(Strings.InnerClassName, resource.IdentifierName);
            invoke2_StringFormat.Parameters.Add(propRef_innerClass);

            foreach(ParamInfo info in paraminfos)
            {
                code.AddParamComments(method, info.ParamName, info.ParamComments);
                CodeVariableReferenceExpression arg2_arg = new CodeVariableReferenceExpression(info.ParamName);
                invoke2_StringFormat.Parameters.Add(arg2_arg);
            }

            method.Statements.Add(new CodeMethodReturnStatement(invoke2_StringFormat));
        }

        private void CreateStringWrapperProperty(ResourceData resource, CodeGen code, CodeTypeDeclaration classDeclaration)
        {
            // 1. public static string {methodName}
            //    {
            CodeMemberProperty property =
                code.AddProperty(classDeclaration, typeof(string), resource.IdentifierName, true, false,
                    ClassVisibility, Static.Yes);

            CreateStringSummaryComments(code, resource, property, false);

            // 2. get { return Raw.{methodName}; }
            //    }
            CodePropertyReferenceExpression propRef_innerClass = code.CreateStaticPropertyCall(Strings.InnerClassName, resource.IdentifierName);
            property.GetStatements.Add(new CodeMethodReturnStatement(propRef_innerClass));
        }

        private bool CreateResourceWrapperProperty(ResourceData resource, CodeGen code, CodeTypeDeclaration classDeclaration)
        {
            try
            {
                Type resType = resource.ResType;

                if(resType == null)
                    return false;

                if(resType == typeof(MemoryStream))
                    resType = typeof(UnmanagedMemoryStream);

                while(resType.IsPublic == false)
                    resType = resType.BaseType;

                bool isStringType = resType == typeof(string);
                bool isStreamType = resType == typeof(MemoryStream) || resType == typeof(UnmanagedMemoryStream);
                string methodName;

                if(isStringType)
                    methodName = "GetString";
                else if(isStreamType)
                    methodName = "GetStream";
                else
                    methodName = "GetObject";

                // 1. public static {resType} {methodName}
                //    {
                //       get {
                CodeMemberProperty property =
                    code.AddProperty(classDeclaration, resType, resource.IdentifierName, true, false,
                        ClassVisibility, Static.Yes);

                if (isStringType)
                {
                    CreateStringSummaryComments(code, resource, property, false);
                }
                else
                {
                    code.AddSummaryComments(property, string.Format(CultureInfo.CurrentCulture, Strings.NonStringResXPropertyDocComments,
                                resource.ResName));
                }
                CodeMethodInvokeExpression invoke_resourceManager =
                    code.CreateMethodCall(Strings.ResourceManagerPropertyName, methodName);
                CodePrimitiveExpression arg_resXName = new CodePrimitiveExpression(resource.ResName);

                invoke_resourceManager.Parameters.Add(arg_resXName);
                invoke_resourceManager.Parameters.Add(Property_CultureInfo);

                CodeExpression exp_return;
                if (isStringType || isStreamType)
                {
                    // 2. return ResourceManager.{GetString|GetStream} ("{resourceName}", Culture);
                    //    }
                    // }
                    exp_return = invoke_resourceManager;
                }
                else
                {
                    // 2. object obj = ResourceManager.GetObject("{resourceName}", Culture);
                    //    return ({resType}) temp;
                    //    }
                    // }
                    CodeVariableDeclarationStatement var_temp = new CodeVariableDeclarationStatement(typeof(object), "obj");
                    CodeVariableReferenceExpression varRef_temp = new CodeVariableReferenceExpression("obj");

                    property.GetStatements.Add(var_temp);
                    var_temp.InitExpression = invoke_resourceManager;
                    exp_return = new CodeCastExpression(new CodeTypeReference(resType), varRef_temp);
                }

                property.GetStatements.Add(new CodeMethodReturnStatement(exp_return));

                return true;
            }
            catch(Exception e)
            {
                ReportError(e.Message);
                return false;
            }
        }

        private static void CreateStringSummaryComments(CodeGen code, ResourceData resource, CodeTypeMember typeMember, bool isMethod)
        {
            string resStringDescription = SecurityElement.Escape(resource.ResString);
            string truncatedMessage = string.Empty;
            string summaryComment;

            if(resStringDescription != null)
            {
                if (resStringDescription.Length > DocCommentResStringLengthThreshold)
                {
                    truncatedMessage = Strings.StringResXDocCommentTruncated;
                    resStringDescription = resStringDescription.Substring(0, DocCommentResStringLengthThreshold);
                }
            }

            if (isMethod)
                summaryComment =
                    string.Format(CultureInfo.CurrentCulture, Strings.StringResXMethodDocComments,
                        resStringDescription, truncatedMessage);
            else
                summaryComment = string.Format(CultureInfo.CurrentCulture, Strings.StringResXPropertyDocComments,
                            resStringDescription, truncatedMessage);

            code.AddSummaryComments(typeMember, summaryComment);
        }

        private void ReportAnyInvalidResources(TextWriter codeStream)
        {
            if (InvalidResources.Count == 0)
                return;

            GeneratorErrorDirective(codeStream, Strings.CustomToolError);

            foreach (ResourceError error in InvalidResources)
            {
                Point location = error.Item.ResLocation;

                ReportError(error.Message, (uint) location.X, (uint) location.Y);
            }
        }

        private static void GeneratorErrorDirective(TextWriter codeStream, string message)
        {
            codeStream.Write('\n' + "#error - " + message);
        }

        private static CodeFieldReferenceExpression Field_ResourceManager
        {
            get { return new CodeFieldReferenceExpression(null, Strings.ResourceManagerVariableName); }
        }

        private static CodeFieldReferenceExpression Field_CultureInfo
        {
            get { return new CodeFieldReferenceExpression(null, Strings.CultureInfoVariableName); }
        }

        private static CodeVariableReferenceExpression Property_CultureInfo
        {
            get { return new CodeVariableReferenceExpression(Strings.CultureInfoPropertyName); }
        }

        private string CustomToolName { get; set; }
        private Visibility ClassVisibility { get; set; }
        private Partial PartialClass { get; set; }
        private IList<ResourceError> InvalidResources { get; set; }
        private string ClassName { get; set; }
        private string ClassNamespace { get; set; }
        private string ResourceNamespace { get; set; }

        private const int DocCommentResStringLengthThreshold = 500;
    }
}